// Copyright (c) 2023 CHANGLEI. All rights reserved.

// ignore_for_file: avoid_redundant_argument_values

part of 'arith.dart';

/// Created by changlei on 2023/6/20.
///
/// 公式错误
final class ArithError extends Error {
  /// 公式错误
  ArithError._([this.message, this._stackTrace]);

  /// 更具[expression]生成指示器
  factory ArithError._expression(
    Object? message,
    String expression,
    int index, [
    int? end,
    StackTrace? stackTrace,
  ]) {
    return ArithError._(
      messageBy(message, expression, index, end),
      stackTrace,
    );
  }

  /// 根据[current]生成
  factory ArithError._factor(
    Object? message,
    Iterable<Factor> factors,
    int index,
    Describable current, [
    StackTrace? stackTrace,
  ]) {
    return ArithError._expression(
      message,
      factors.toDescribe(_isTight),
      index + current.toTightRange(_isTight).$1,
      index + current.toTightRange(_isTight).$2,
      stackTrace,
    );
  }

  /// 给[e]加指示器
  factory ArithError.error(
    ArithError error,
    Iterable<Factor> factors,
    int index,
    Describable current, [
    StackTrace? stackTrace,
  ]) {
    return ArithError._factor(
      error.message,
      factors,
      index,
      current,
      stackTrace,
    );
  }

  /// 叠加[e]
  factory ArithError.stack(
    ArithError error,
    Iterable<Factor> factors,
    int index, [
    StackTrace? stackTrace,
  ]) {
    final lines = error.message.toString().split('\n');
    if (lines.length >= 3) {
      final message = lines[0];
      final indicator = lines[2].substring(_indent.length);
      final expression = factors.toDescribe(_isTight);
      final start = index + indicator.indexOf(_indicator);
      final end = index + indicator.lastIndexOf(_indicator);
      return ArithError._expression(message, expression, start, end + 1, stackTrace);
    } else {
      return error;
    }
  }

  /// 括号异常
  factory ArithError.curve(
    String expression,
    int index, [
    int? end,
    int? depth,
    StackTrace? stackTrace,
  ]) {
    return ArithError._expression(
      switch (depth ?? 0) {
        < 0 => "Can't find ')' to match '('.",
        > 0 => "Can't find '(' to match ')'.",
        _ => "Invalid expression, the number of '(' is not equal to the number of ')'.",
      },
      expression,
      index,
      end,
      stackTrace,
    );
  }

  /// 括号异常
  factory ArithError.curveFactor(
    Iterable<Factor> factors,
    int index,
    Describable current, [
    int? depth,
    StackTrace? stackTrace,
  ]) {
    return ArithError.curve(
      factors.toDescribe(_isTight),
      index + current.toTightRange(_isTight).$1,
      index + current.toTightRange(_isTight).$2,
      (() {
        if (depth != null) {
          return depth;
        } else if (current == Curve.left) {
          return 1;
        } else if (current == Curve.right) {
          return -1;
        } else {
          return 0;
        }
      })(),
      stackTrace,
    );
  }

  /// 左括号异常
  factory ArithError.curveLeft(
    Iterable<Factor> factors,
    int index,
    Describable current, [
    StackTrace? stackTrace,
  ]) {
    return ArithError.curveFactor(
      factors,
      index,
      current,
      1,
      stackTrace,
    );
  }

  /// 有括号异常
  factory ArithError.curveRight(
    Iterable<Factor> factors,
    int index,
    Describable current, [
    StackTrace? stackTrace,
  ]) {
    return ArithError.curveFactor(
      factors,
      index,
      current,
      -1,
      stackTrace,
    );
  }

  /// 操作符异常
  factory ArithError.operator(
    Iterable<Factor> factors,
    int index,
    Operator current, [
    StackTrace? stackTrace,
  ]) {
    return ArithError._factor(
      "'$current' is not a prefix operator.",
      factors,
      index,
      current,
      stackTrace,
    );
  }

  /// 函数异常
  factory ArithError.function(
    Iterable<Factor> factors,
    int index,
    Math current, [
    StackTrace? stackTrace,
  ]) {
    return ArithError._factor(
      "Found this candidate, but the arguments don't match.",
      factors,
      index,
      current,
      stackTrace,
    );
  }

  /// 异常字符
  factory ArithError.character(
    String expression,
    int index, [
    int? end,
    StackTrace? stackTrace,
  ]) {
    return ArithError._expression(
      "Expected an identifier, but got '${expression.substring(index, end)}'.",
      expression,
      index,
      end,
      stackTrace,
    );
  }

  /// 变量异常
  factory ArithError.variable(String symbol, [StackTrace? stackTrace]) {
    return ArithError._(
      "Variable '$symbol' must be assigned before it can be used.",
      stackTrace,
    );
  }

  static const _indent = '  ';
  static const _indicator = '^';
  static const _isTight = RPN._isTight;

  /// 错误信息
  final Object? message;

  final StackTrace? _stackTrace;

  @override
  StackTrace? get stackTrace => _stackTrace ?? super.stackTrace;

  @override
  String toString() {
    const String report = 'ArithError';
    final message = this.message;
    if (message == null) {
      return report;
    }
    return '$report: $message';
  }

  /// 生成message
  static String messageBy(Object? message, String expression, int index, [int? end]) {
    final buffer = StringBuffer(message?.toString() ?? '');
    buffer.writeln();
    buffer.write(indicatorBy(expression, index, end));
    return buffer.toString();
  }

  /// 生成指示器
  static String indicatorBy(String expression, int index, [int? end]) {
    final buffer = StringBuffer('$_indent$expression');
    buffer.writeln();
    buffer.write(_indent);
    for (var i = 0; i < expression.length; i++) {
      buffer.write((end == null ? index == i : i >= index && i < end) ? _indicator : ' ');
    }
    return buffer.toString();
  }
}
